﻿// --------------------------------
// <copyright file="Program.cs" company="TRONSoft">
//     Copyright (c) TRONSoft.
// </copyright>
// <license>
//   This source code is subject to terms and conditions of the Microsoft
//   Public License. A copy of the license can be found in the License.html
//   file at the root of this distribution. If you cannot locate the
//   Microsoft Public License, please send an email to dlr@microsoft.com.
//   By using this source code in any fashion, you are agreeing to be bound 
//   by the terms of the Microsoft Public License. You must not remove this
//   notice, or any other, from this software.
// </license>
// <author>Tron</author>
// ---------------------------------

using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using Tronsoft.Actucal.CalculationEngineCreator;
using Tronsoft.Actucal.Tables;

namespace PocRunner
{
    public class Test
    {
        public string Member1;
        public List<string> Member2;
        public DataTable myTable;

        public Test(double renteVoet)
        {
            myTable = new DataTable("myTable");
            myTable.Columns.Add("col1", typeof(double));
            myTable.Columns.Add("col2", typeof(double));
            myTable.Columns.Add("col3", typeof(double));
            myTable.Columns.Add("col4", typeof(double));

            for (int i = 0; i < 100; i++)
            {
                DataRow dr = myTable.NewRow();
                dr["col1"] = 1;
                dr["col2"] = 2 * 4 ;
                dr["col3"] = 3+23;
                dr["col4"] = 5;

                myTable.Rows.Add(dr);
            }    
        }


        public bool IsValid(string toValidate)
        {
            return string.IsNullOrEmpty(toValidate);
        }
    }

    public class UitvoerLijst
    {
        public string Member1;
        public List<string> Member2;
        public DataTable resultTable;

        public UitvoerLijst(double renteVoet)
        {
            resultTable = new DataTable("resultTable");
            resultTable.Columns.Add("col1", typeof(double));
            resultTable.Columns.Add("col2", typeof(double));
            resultTable.Columns.Add("col3", typeof(double));
            resultTable.Columns.Add("col4", typeof(double));

            for (int i = 0; i < 100; i++)
            {
                DataRow dr = resultTable.NewRow();
                dr["col1"] = 1;
                dr["col2"] = 2 * 4;
                dr["col3"] = 3 + 23;
                dr["col4"] = 5;

                resultTable.Rows.Add(dr);
            }
        }

        public bool IsValid(string toValidate)
        {
            return string.IsNullOrEmpty(toValidate);
        }
    }

    public class JaarResultaat
    {
        public string Member1;
        public List<string> Member2;
        public DataTable JaarResultaatTabel;
        public double test;

        public JaarResultaat(int renteVoet)
        {
            test = 5.0;

            JaarResultaatTabel = new DataTable("JaarResultaatTabel");
            JaarResultaatTabel.Columns.Add("Jaar", typeof(int));
            JaarResultaatTabel.Columns.Add("VerzekerdBedrag", typeof(double));

            for (int i = 0; i < 100; i++)
            {
                DataRow dr = JaarResultaatTabel.NewRow();
                dr["Jaar"] = i;
                dr["VerzekerdBedrag"] = i * 3000;

                JaarResultaatTabel.Rows.Add(dr);
            }    
        }

        public bool IsValid(string toValidate)
        {
            return string.IsNullOrEmpty(toValidate);
        }
    }
    
    class Program
    {
        public const string START_CALCULATION_NAME = "main";

        private static CalculationModel model = new CalculationModel();

        /// <summary>
        /// Put the definition of the model here.
        /// </summary>
        private static void InitializeModel()
        {
            // Add namespace
            model.AddInclude(@"PocRunner");
            model.AddReference(typeof(Test).Assembly.Location);
            model.AddReference(typeof(JaarResultaat).Assembly.Location);

            // Table
            TableDefinition table = TableManager.Instance.AddTable("Table1");
            table.Columns.Add("Lengte", typeof(double));
            table.Columns.Add("Breedte", typeof(double));
            table.Columns.Add("Hoogte", typeof(double));

            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("PocRunner.table1.csv"))
            {
                StreamReader reader = new StreamReader(stream);
                table.AddTableRows(reader, true, CultureInfo.InvariantCulture);
            }

            // sterftetabel
            TableDefinition Sterftetabel8590_ = TableManager.Instance.AddTable("Sterftetabel8590");
            Sterftetabel8590_.Columns.Add("Leeftijd", typeof(int));
            Sterftetabel8590_.Columns.Add("Man", typeof(int));
            Sterftetabel8590_.Columns.Add("Vrouw", typeof(int));

            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("PocRunner.Sterftetabel8590.csv"))
            {
                StreamReader reader = new StreamReader(stream);
                Sterftetabel8590_.AddTableRows(reader, true, CultureInfo.InvariantCulture);
            }

             // Constant
            model.AddConstant("cnstProduct", "Annuitair"); //  Gelijkblijvend; Annuitair; Lineair Dalend
            model.AddConstant("cnstRentevoet", 0.3);
            model.AddConstant("cnstOK", 1);
            model.AddConstant("cnstFAILED", 0);
            model.AddConstant("cnst_i", 0.03);
            model.AddConstant("constBF", 1.65);
            model.AddConstant("cnstKst", 45.38);
            model.AddConstant("cnst_j", 0.03);
            model.AddConstant("cnstDEBUG", false);

             // methodsignature
            MethodSignature methodSig = new MethodSignature();
            methodSig.AddParameter("i", typeof(int));
            methodSig.ReturnValue = typeof(double);
            
            MethodSignature methodSig_prae_ax  = new MethodSignature();
            methodSig_prae_ax.AddParameter("int32Duur", typeof(Int32));
            methodSig_prae_ax.AddParameter("dblRente", typeof(double));
            methodSig_prae_ax.ReturnValue = typeof(double);

            MethodSignature methodSig_gr_an = new MethodSignature();
            methodSig_gr_an.AddParameter("int32Duur", typeof(Int32));
            methodSig_gr_an.AddParameter("dblRente", typeof(double));
            methodSig_gr_an.ReturnValue = typeof(double);

            MethodSignature methodSigStr = new MethodSignature();
            methodSigStr.AddParameter("i", typeof(int));
            methodSigStr.ReturnValue = typeof(string);

            // invoerParameter
            model.AddCalculationParameter<double>("invoerPar_Tarief", 1.00);
            model.AddCalculationParameter<string>("invoerPar_Geslacht", "m");
            model.AddCalculationParameter<string>("invoerPar_Geboortedatum", "01/01/1990");
            model.AddCalculationParameter<string>("invoerPar_Ingangsdatum", "01-01-2008");
            model.AddCalculationParameter<int>("invoerPar_LooptijdInJaren", 0);
            model.AddCalculationParameter<int>("invoerPar_VerzekerdBedrag", 1000000);
            model.AddCalculationParameter<int>("invoerPar_PremieBetalingstermijn", 3); //uitgedrukt in maanden

            // uitvoerParameter
            model.AddCalculationParameter<double>("uitvoerPar_Termijnpremie", 1);
            model.AddCalculationParameter<double>("uitvoerPar_Brutojaarpremie", 1.0);
            model.AddCalculationParameter<string>("uitvoerPar_geef_Wisseldatum_verzekerde_bedragen", "01-01-2001");
            model.AddCalculationParameter<double>("uitvoerPar_Jaarlijkse_verzekerde_bedragen", 1.0);
            
            // Parameter
            model.AddCalculationParameter<int>("par_geslachtindex", 1);
            model.AddCalculationParameter<double>("par_v", 0.0);
            model.AddCalculationParameter<double>("par_Lx", 0.0);
            model.AddCalculationParameter<double>("par_NettoJaarpremie", 0.0);
            model.AddCalculationParameter<double>("par_Lijfrente", 0.0);
            model.AddCalculationParameter<double>("par_NettoKoopsom", 0.0);
            model.AddCalculationParameter<double>("par_BrutoMaandPremie", 0.0);
            model.AddCalculationParameter<double>("par_BrutoKwartaalPremie", 0.0);
            model.AddCalculationParameter<double>("par_BrutoPremieHalfJaar", 0.0);
            model.AddCalculationParameter<long>("par_LeeftijdX", 0);

            model.AddCalculation("calc_par", @"

                par_v = 1/ (1+ cnst_i)
                par_NettoJaarpremie=0.0
                par_NettoJaarpremie = 0
                par_NettoJaarpremie=0.0
                par_BrutoMaandPremie=0.0
                par_BrutoKwartaalPremie=0.0
                par_BrutoPremieHalfJaar=0.0
            
                if (invoerPar_Geslacht = ""V"") then
                    par_geslachtindex = 2
                else
                    par_geslachtindex = 1
                end if

                return cnstOk  
            ");

            model.AddCalculation("LeesInvoer", @"

                invoerPar_Tarief =  1.00
                invoerPar_Geslacht = ""V""
                invoerPar_LooptijdInJaren = 30 
                invoerPar_VerzekerdBedrag =  1000000
               
                return cnstOk
            ");

            model.AddMethod("IsValid", methodSig, "return i + invoerPar_VerzekerdBedrag");

            //========================================================================
            //=  Functienaam       : Gr_An (een grote An)
            //=  Doel              : Berekenen contante waarde van een bedrag 1 na een aantal jaar
            //=  Creatiedatum      : 
            //=  Auteur            : AvK
            //========================================================================
            //=  Parameter(s)
            //=  dblDuur           : Looptijd waarover de contante waarde berekend moet worden
            //=  dblRente          : Rekenrente
            //========================================================================
            //=  Returnwaarde
            //=  Contante waarde van een bedrag 1 na een aantal jaar
            //========================================================================

            model.AddMethod("Gr_an", methodSig_gr_an, @"

                return ((convert.todouble(1.0)  + (convert.todouble(dblRente) / 100.0)) ^ (convert.todouble(int32Duur) * -1.0))
                
            "); 

            //implementatie actuariele functie

            //========================================================================
            //=  Functienaam       : prae_an (een prenumarando kleine an)
            //=  Doel              : Berekenen van de contante waarde bij een jaarlijkse inleg van 1 en
            //=                      waarbij de eerste inleg direct plaatsvindt
            //=  Creatiedatum      : 
            //=  Auteur            : AvK
            //========================================================================
            //=  Parameter(s)
            //=  dblDuur           : Looptijd gedurende waarin er jaarlijks een inleg van 1 plaatsvindt
            //=  dblRente          : Rente waarmee de inleg jaarlijks contant gemaakt moet worden
            //========================================================================
            //=  Returnwaarde
            //=  Slotwaarde bij een jaarlijkse inleg van 1
            //========================================================================
            // toevoegen regel 270 dblBedrag = 1 - Gr_An(dblDuur, dblDuur)
            model.AddMethod("act_prae_ax", methodSig_prae_ax, @"

              Dim dblBedrag As Double

              If dblRente <> 0 Then
                dblBedrag = convert.todouble(1.0) - convert.todouble(Gr_An(3, cnst_i))
                dblBedrag = dblBedrag * (1.0 + (convert.todouble(cnstRentevoet) / 100))
                dblBedrag = dblBedrag / (cnstRentevoet / 100)
                return dblBedrag
              Else
                return  convert.todouble(int32Duur)
              End If
                
            ");

            model.AddCalculation("bereken_gr_an", @"

                return convert.todouble(Gr_An(3, cnst_i))

            ");

            model.AddCalculation("actTest", @"

                return act_prae_ax( 3, cnst_i)
            ");


            // invoer parameters tonen
            model.AddCalculation("print_invoerparameters", @"
            
                Console.WriteLine(""invoer Tarief: {0}"", invoerPar_Tarief)
                Console.WriteLine(""invoer Geslacht: {0}"", invoerPar_Geslacht)
                Console.WriteLine(""invoer Geboortedatum: {0}"", invoerPar_Geboortedatum)
                Console.WriteLine(""invoer Ingangsdatum: {0}"", invoerPar_Ingangsdatum)
                Console.WriteLine(""invoer LooptijdInJaren: {0}"", invoerPar_LooptijdInJaren)
                Console.WriteLine(""invoer VerzekerdBedrag: {0}"", invoerPar_VerzekerdBedrag)
                Console.WriteLine(""invoer PremieBetalingstermijn: {0}"", invoerPar_PremieBetalingstermijn)
            ");

            model.AddCalculation("calcTermijnPremie", @"
                
                select case invoerPar_PremieBetalingstermijn
                    case 1:     uitvoerPar_Termijnpremie = calcBrutoMaandPremie
                    case 3:     uitvoerPar_Termijnpremie = calcBrutoKwartaalPremie
                    case 12:    uitvoerPar_Termijnpremie = calcBrutoJaarPremie
                end select

                return uitvoerPar_Termijnpremie

            ");

            model.AddMethod("Ck", methodSig, @"

                select case cnstProduct
                    case ""Gelijkblijvend"" : return invoerPar_VerzekerdBedrag
                    case ""Lineair Dalend"" : return invoerPar_VerzekerdBedrag - (invoerPar_VerzekerdBedrag/invoerPar_LooptijdInJaren)*i 
                    case ""Annuitair""      : return invoerPar_VerzekerdBedrag * ((1-(par_v^(invoerPar_LooptijdInJaren-i)))/(1-(par_v^(invoerPar_LooptijdInJaren))))
                end select

                return 0
            ");

            model.AddMethod("Wisseldatum", methodSigStr, @"
                
                uitvoerPar_geef_Wisseldatum_verzekerde_bedragen = Cstr(Microsoft.VisualBasic.DateAndTime.DateAdd(""yyyy"", i, CDate(invoerPar_Ingangsdatum)))
                return uitvoerPar_geef_Wisseldatum_verzekerde_bedragen
            ");
            
            model.AddCalculation("calcNettoKoopsom", @"

               For i as Integer = 0 To convert.toint32(invoerPar_LooptijdInJaren)-1
                  par_NettoKoopsom = par_NettoKoopsom + Ck(i) * par_v ^(Convert.todouble(i) + 0.5) * (Convert.ToInt32(Sterftetabel8590(i + Convert.ToInt32(par_LeeftijdX), par_geslachtindex))-Convert.ToInt32(Sterftetabel8590(i + Convert.ToInt32(par_LeeftijdX)+1, par_geslachtindex)))/Convert.ToInt32(Sterftetabel8590(Convert.ToInt32(par_LeeftijdX), par_geslachtindex))
               next

               return par_NettoKoopsom  
            ");

            model.AddCalculation("calcLijfrente", @"

                For i as Integer = 0 To convert.toint32(invoerPar_LooptijdInJaren)-6
                  par_Lijfrente = par_Lijfrente + par_v^(i) * (Convert.ToInt32(Sterftetabel8590(i + Convert.ToInt32(par_LeeftijdX), par_geslachtindex))/Convert.ToInt32(Sterftetabel8590(Convert.ToInt32(par_LeeftijdX), par_geslachtindex))) 
                next

                return par_Lijfrente
            ");

            model.AddCalculation("calcNettoJaarpremie", @"

                if (calcLijfrente <>0) then
                    par_NettoJaarpremie = convert.todouble(calcNettoKoopsom)/convert.todouble(par_Lijfrente)
                else
                    par_NettoJaarpremie = 0
                end if

                return par_NettoJaarpremie  
            ");

            model.AddCalculation("calcBrutoMaandPremie", @"

                dim C_bmp as double
                C_bmp = 0.001 * Ck(0) * 0.088 / 0.85

                par_BrutoMaandPremie =  (calcNettoJaarpremie * convert.todouble(constBF) + convert.todouble(cnstKst))*0.088 + C_bmp
                return par_BrutoMaandPremie
            ");

            model.AddCalculation("calcBrutoHalfJaarPremie", @"

                par_BrutoPremieHalfJaar =  par_BrutoMaandPremie * 5.796
                return par_BrutoPremieHalfJaar
            ");

            model.AddCalculation("calcBrutoKwartaalPremie", @"

                par_BrutoKwartaalPremie =  par_BrutoMaandPremie * 2.932
                return par_BrutoKwartaalPremie
            ");

            model.AddCalculation("calcBrutoJaarPremie", @"

                uitvoerPar_Brutojaarpremie =  par_BrutoMaandPremie * 11.364
                return uitvoerPar_Brutojaarpremie
            ");

            model.AddCalculation("calcLeeftijd", @"

                par_LeeftijdX = Microsoft.VisualBasic.DateAndTime.datediff(Microsoft.VisualBasic.DateInterval.Year,CDate(invoerPar_Geboortedatum),CDate(invoerPar_Ingangsdatum))

                return 1.0
            ");

            model.AddCalculation("PocBerekening", @"

                'DEBUG
                if cnstDEBUG then   
                console.writeline(""start PocBerekening"")
                end if

                dim blnResult as double
                blnResult  = calc_par
                blnResult  = calcLeeftijd

                'DEBUG
                if cnstDEBUG then
                Console.WriteLine(""par_LeeftijdX {0}"", par_LeeftijdX)
                end if

                par_BrutoMaandPremie = calcBrutoMaandPremie

                'DEBUG
                if cnstDEBUG then
                Console.WriteLine(""par_BrutoMaandPremie {0}"", par_BrutoMaandPremie)
                end if

                par_BrutoPremieHalfJaar = calcBrutoHalfJaarPremie

                'DEBUG
                if cnstDEBUG then
                Console.WriteLine(""par_BrutoPremieHalfJaar {0}"", par_BrutoPremieHalfJaar)
                end if

                par_BrutoKwartaalPremie = calcBrutoKwartaalPremie

                'DEBUG
                if cnstDEBUG then
                Console.WriteLine(""par_BrutoKwartaalPremie  {0}"", par_BrutoKwartaalPremie )
                end if

                uitvoerPar_Brutojaarpremie = calcBrutoJaarPremie
                uitvoerPar_Termijnpremie = calcTermijnPremie

                return  cnstOK
            ");

            // parameter uitvoerfuncties
            model.AddCalculation("print_Termijnpremie", @"

                Console.WriteLine(""uitvoerPar_Termijnpremie: {0}"", uitvoerPar_Termijnpremie)
                return cnstOK  

            ");

            // parameter uitvoerfuncties
            model.AddCalculation("print_Jaarpremie", @"

                Console.WriteLine(""uitvoerPar_Brutojaarpremie: {0}"", uitvoerPar_Brutojaarpremie)

            ");

            model.AddCalculation("print_Jaarlijkse_verzekerde_bedragen", @"

                for  j as integer = 0 to invoerPar_LooptijdInJaren
                    Console.WriteLine("" wisseldatum: {0}  looptijd (jaren): {1} verz bedr:{2}"", Wisseldatum(j), j, Convert.toint32(Ck(j)))
                next

                return cnstOk

            ");

        }

        static void Main(string[] args)
        {
            Console.WriteLine("POC initialisatie...");
            
            // voorbereiden sterftetabellen, N(x) e.d.
            InitializeModel();

            // Setup a stopwatch
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            
            // Do the calculation
            double result = 0.0;

            // reset calculationparameters
            result = model.Eval("LeesInvoer");

            result = model.Eval("print_invoerparameters");

            // teruggeven van calculation parameters
            for (int i = 0; i < 100; i++)
            {

                result = model.Eval("PocBerekening");
                
            }
            result = model.Eval("print_Termijnpremie");
            result = model.Eval("print_Jaarpremie");
            result = model.Eval("print_Jaarlijkse_verzekerde_bedragen");

            stopwatch.Stop();

            Console.WriteLine("Einde berekening");

            double elapsedTime = (double)stopwatch.ElapsedMilliseconds/105;
            Console.WriteLine("De berekening nam {0:N} milliseconden in beslag", elapsedTime);

            Console.ReadKey();
        }
    }
}
